package com.xuecheng.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.xuecheng.commons.model.dto.CoursePubIndexDto;
import com.xuecheng.commons.model.dto.PageRequest;
import com.xuecheng.commons.model.vo.CoursePubIndexVo;
import com.xuecheng.commons.model.vo.CoursePubVo;
import com.xuecheng.commons.model.vo.PageResponseResult;
import com.xuecheng.commons.model.vo.ResponseResult;
import com.xuecheng.commons.utils.BeanHelper;
import com.xuecheng.content.api.ContentClient;
import com.xuecheng.search.domain.CoursePubIndex;
import com.xuecheng.search.service.SearchService;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private ContentClient contentClient;

    /**
     * 课程搜索
     */
    public PageResponseResult<CoursePubIndexVo> search(CoursePubIndexDto dto,
                                                       PageRequest request) throws Exception {
        //1、构建查询条件
        //1.1 创建SearchRequest对象
        SearchRequest searchRequest = new SearchRequest("course");
        //1.2 创建查询条件
        SearchSourceBuilder builder = generatorBuilder(dto, request);
        //1.3 将查询条件，设置到SearchRequest对象中
        searchRequest.source(builder);
        //2、发起请求
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //3、解析数据结果
        return this.parseResponse(response);
    }

    //同步es索引库
    public void updateIndex(Long coursePubId) throws Exception {
        //调用feign接口，根据id查询课程发布对象：CoursePubVo
        ResponseResult<CoursePubVo> result = contentClient.findById(coursePubId);
        CoursePubVo vo = result.getData();
        //创建IndexRequest对象
        IndexRequest indexRequest = new IndexRequest("course");
        //设置id，设置保存的JSON数据(ES索引库中接受的对象，CoursePubIndex)
        indexRequest.id(vo.getId().toString());
        CoursePubIndex index = BeanHelper.copyProperties(vo, CoursePubIndex.class);
        index.setCourse_id(vo.getCourseId());
        index.setCompany_id(vo.getCompanyId());
        indexRequest.source(JSON.toJSONString(index), XContentType.JSON);
        //调用client保存
        restHighLevelClient.index(indexRequest,RequestOptions.DEFAULT);
    }

    //根据id查询课程发布数据
    public ResponseResult<CoursePubIndexVo> findById(Long id) throws IOException {
        //构造GetRequest请求
        GetRequest request = new GetRequest("course",id.toString());
        //发送请求
        GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
        //解析响应
        String json = response.getSourceAsString();
        //转化CoursePubIndex
        CoursePubIndex index = JSON.parseObject(json, CoursePubIndex.class);
        CoursePubIndexVo vo = BeanHelper.copyProperties(index, CoursePubIndexVo.class);
        //vo中需要的indexId，es对象中叫做id
        vo.setIndexId(index.getId());
        return ResponseResult.okResult(vo);
    }


    /**
     * 构建查询条件
     */
    public SearchSourceBuilder generatorBuilder(CoursePubIndexDto dto, PageRequest request) {
        //创建builder
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //分页参数
        builder.from((request.getPageNo() - 1) * request.getPageSize());
        builder.size(request.getPageSize());
        //bool条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        String keyword = dto.getKeyword(); //搜索关键字
        if(StringUtils.isEmpty(keyword)) {
            boolQuery.must(QueryBuilders.matchAllQuery());
        }else {
            boolQuery.must(QueryBuilders.matchQuery("all",keyword));
        }
        //添加筛选条件
        //大分类
        String mt = dto.getMt();
        if(!StringUtils.isEmpty(mt)) {
            boolQuery.filter(QueryBuilders.termQuery("mt",mt));
        }
        //小分类
        String st = dto.getSt();
        if(!StringUtils.isEmpty(st)) {
            boolQuery.filter(QueryBuilders.termQuery("st",st));
        }
        //课程难度
        String grade = dto.getGrade();
        if(!StringUtils.isEmpty(grade)) {
            boolQuery.filter(QueryBuilders.termQuery("grade",grade));
        }
        builder.query(boolQuery);

        //设置高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font color=red>").postTags("</font>")
                    .field("name").requireFieldMatch(false);
        builder.highlighter(highlightBuilder);

        return builder;
    }

    /**
     * 解析响应数据
     */
    public PageResponseResult parseResponse(SearchResponse response) {
        //总数：response->hits->total
        SearchHits hits = response.getHits();
        Long total = hits.getTotalHits().value;
        //list集合: response ->hits -->hits数组 -->source (对象json字符串)
        SearchHit[] hitsHits = hits.getHits();
        List<CoursePubIndexVo> vos = new ArrayList<>();
        for (SearchHit hitsHit : hitsHits) {
            String json = hitsHit.getSourceAsString(); //source
            //index对象中的name属性，还是未设置高亮的状态
            CoursePubIndex index = JSON.parseObject(json, CoursePubIndex.class);
            //解析出高亮部分的name属性，替换到index中
            Map<String, HighlightField> map = hitsHit.getHighlightFields();
            if(map != null && map.containsKey("name")) {
                HighlightField nameField = map.get("name");
                String name = nameField.fragments()[0].toString();
                index.setName(name);
            }
            //将es对象转化成vo
            CoursePubIndexVo vo = BeanHelper.copyProperties(index, CoursePubIndexVo.class);
            vo.setIndexId(index.getId());
            vos.add(vo);
        }
        return new PageResponseResult(total,vos);
    }
}
